unit uniEnemyAmmo;

interface

uses
  uniDefine,
  uniAmmo,
  uniHeroAircraft,
  // uniGlobal,
  SysUtils,
  gl,
  Classes;

type
  { EnemyAmmo }

  EnemyAmmo = class(TObject)
  public
    ammoRoot: array[0..NUM_ENEMY_AMMO_TYPES - 1] of ActiveAmmo;
    ammoPool: ActiveAmmo;
    ammoSize: array[0..NUM_ENEMY_AMMO_TYPES - 1, 0..1] of Single;
    ammoDamage: array[0..NUM_ENEMY_AMMO_TYPES - 1] of Single;
    ammoTex: array[0..NUM_ENEMY_AMMO_TYPES - 1] of GLuint;

    // game: Global;

    constructor Create;
    destructor Destroy; override;

    procedure addAmmo(_type: Integer; pos: array of Single; vel: array of Single);
    procedure updateAmmo;

    procedure checkForHits(hero: HeroAircraft);

    procedure drawGL;
    procedure clear;

    procedure loadTextures;
    procedure deleteTextures;

    function getNewAmmo: ActiveAmmo;
    procedure killAmmo(dead: ActiveAmmo);

  end;


implementation

uses
  PNGLoader,
  uniConfig,
  uniExplosions,
  uniGlobal;

{ EnemyAmmo }

procedure EnemyAmmo.addAmmo(_type: Integer; pos, vel: array of Single);
var
  newAmmo: ActiveAmmo;
  first: ActiveAmmo;
  v: array[0..2] of Single;
begin
  if (_type >= 0) and (_type < NUM_ENEMY_AMMO_TYPES) then
  begin
    v[0] := vel[0] * uniGlobal.speedAdj;
    v[1] := vel[1] * uniGlobal.speedAdj;
    v[2] := vel[2] * uniGlobal.speedAdj;
    newAmmo := getNewAmmo;
    newAmmo.init(pos, v, ammoDamage[_type]);
    newAmmo.back := ammoRoot[_type];
    newAmmo.next := ammoRoot[_type].next;
    if Boolean(ammoRoot[_type].next) then
    begin
      first := ammoRoot[_type].next;
      first.back := newAmmo;
    end;
    ammoRoot[_type].next := newAmmo;
  end;
end;

procedure EnemyAmmo.checkForHits(hero: HeroAircraft);
var
  i: Integer;
  thisAmmo: ActiveAmmo;
  backAmmo: ActiveAmmo;
  nextAmmo: ActiveAmmo;
  minDist: Single;
  dist: Single;
  p: PSingle;
  _explo: Explo;
begin
  minDist := (hero.getSize(0) + hero.getSize(1)) * 0.5;

  if not hero.isVisible then Exit;

  //-- Go through all the ammunition and check for hits
  for i := 0 to NUM_ENEMY_AMMO_TYPES - 1 do
  begin
    thisAmmo := ammoRoot[i].next;
    while Boolean(thisAmmo) do
    begin
      p := @thisAmmo.pos;
      dist := Abs(p^ - hero.pos[0]); Inc(p);
      dist := dist + Abs(p^ - hero.pos[1]);
      if dist < minDist then
      begin
        // do damage
        hero.ammoDamage(ammoDamage[i], thisAmmo.vel);
        // add explosion
        _explo := uniGlobal._explosions.addExplo(ExploType(Integer(EnemyAmmo00) + i), thisAmmo.pos);
        if i > 1 then // add second explosion for the bug guns...
          _explo := uniGlobal._explosions.addExplo(ExploType(Integer(EnemyAmmo00) + i), thisAmmo.pos, -5)
        else
          if Boolean(_explo) then _explo.vel[1] := -0.1;

        backAmmo := thisAmmo.back;
        nextAmmo := thisAmmo.next;
        backAmmo.next := nextAmmo;
        if Boolean(nextAmmo) then
          nextAmmo.back := backAmmo;
        killAmmo(thisAmmo);
        thisAmmo := backAmmo;
      end;
      thisAmmo := thisAmmo.next;
    end;
  end;
end;

procedure EnemyAmmo.clear;
var
  i: Integer;
  cur: ActiveAmmo;
  del: ActiveAmmo;
begin
  for i := 0 to NUM_ENEMY_AMMO_TYPES - 1 do
  begin
    cur := ammoRoot[i].next;
    while Boolean(cur) do
    begin
      del := cur;
      cur := cur.next;
      killAmmo(del);
    end;
    ammoRoot[i].next := nil;
  end;
end;

constructor EnemyAmmo.Create;
var
  game: Global;
  i: Integer;
begin
  game := uniGlobal.getInstance;
  //-- initialize everything to sane values...
  for i := 0 to NUM_ENEMY_AMMO_TYPES - 1 do
  begin
    ammoRoot[i] := ActiveAmmo.Create;
    ammoSize[i][0] := 0.13;
    ammoSize[i][1] := 0.13;
    ammoDamage[i] := 510.0;
  end;

  ammoPool := ActiveAmmo.Create;

  loadTextures;

  ammoDamage[0] := 75.0;
  ammoSize[0][0] := 0.25;
  ammoSize[0][1] := 0.55;

  ammoDamage[1] := 6.0;
  ammoSize[1][0] := 0.225;
  ammoSize[1][1] := 0.225;

  ammoDamage[2] := 100.0;
  ammoSize[2][0] := 0.45;
  ammoSize[2][1] := 0.45;

  ammoDamage[3] := 20.0;
  ammoSize[3][0] := 0.3;
  ammoSize[3][1] := 0.5;

  ammoDamage[4] := 8.5;
  ammoSize[4][0] := 0.12;
  ammoSize[4][1] := 0.5;
end;

procedure EnemyAmmo.deleteTextures;
var
  i: Integer;
begin
  for i := 0 to NUM_ENEMY_AMMO_TYPES - 1 do
  begin
    glDeleteTextures(1, @ammoTex[i]);
    ammoTex[i] := 0;
  end;
end;

destructor EnemyAmmo.Destroy;
var
  cur: ActiveAmmo;
  del: ActiveAmmo;
  i: Integer;
begin
  clear;
  cur := ammoPool.next;
  while Boolean(cur) do
  begin
    del := cur;
    cur := cur.next;
    del.Destroy;
  end;

  deleteTextures;

  for i := 0 to NUM_ENEMY_AMMO_TYPES - 1 do
  begin
    ammoRoot[i].Destroy;
  end;

  ammoPool.Destroy;
end;

procedure EnemyAmmo.drawGL;
var
  i: Integer;
  pos: array of Single;
  thisAmmo: ActiveAmmo;
begin
  for i := 0 to NUM_ENEMY_AMMO_TYPES - 1 do
  begin
    glColor4f(1.0, 1.0, 1.0, 1.0);
    glBindTexture(GL_TEXTURE_2D, ammoTex[i]);
    thisAmmo := ammoRoot[i].next;
    glBegin(GL_QUADS);
    while Boolean(thisAmmo) do
    begin
      pos := @thisAmmo.pos;
      case IRAND mod 4 of
        0:
          begin
            glTexCoord2f(0.0, 0.0); glVertex3f(pos[0] - ammoSize[i][0], pos[1] + ammoSize[i][1], pos[2]);
            glTexCoord2f(0.0, 1.0); glVertex3f(pos[0] - ammoSize[i][0], pos[1] - ammoSize[i][1], pos[2]);
            glTexCoord2f(1.0, 1.0); glVertex3f(pos[0] + ammoSize[i][0], pos[1] - ammoSize[i][1], pos[2]);
            glTexCoord2f(1.0, 0.0); glVertex3f(pos[0] + ammoSize[i][0], pos[1] + ammoSize[i][1], pos[2]);
          end;
        1:
          begin
            glTexCoord2f(1.0, 0.0); glVertex3f(pos[0] - ammoSize[i][0], pos[1] + ammoSize[i][1], pos[2]);
            glTexCoord2f(1.0, 1.0); glVertex3f(pos[0] - ammoSize[i][0], pos[1] - ammoSize[i][1], pos[2]);
            glTexCoord2f(0.0, 1.0); glVertex3f(pos[0] + ammoSize[i][0], pos[1] - ammoSize[i][1], pos[2]);
            glTexCoord2f(0.0, 0.0); glVertex3f(pos[0] + ammoSize[i][0], pos[1] + ammoSize[i][1], pos[2]);
          end;
        2:
          begin
            glTexCoord2f(0.0, 1.0); glVertex3f(pos[0] - ammoSize[i][0], pos[1] + ammoSize[i][1], pos[2]);
            glTexCoord2f(0.0, 0.0); glVertex3f(pos[0] - ammoSize[i][0], pos[1] - ammoSize[i][1], pos[2]);
            glTexCoord2f(1.0, 0.0); glVertex3f(pos[0] + ammoSize[i][0], pos[1] - ammoSize[i][1], pos[2]);
            glTexCoord2f(1.0, 1.0); glVertex3f(pos[0] + ammoSize[i][0], pos[1] + ammoSize[i][1], pos[2]);
          end;
        3:
          begin
            glTexCoord2f(1.0, 1.0); glVertex3f(pos[0] - ammoSize[i][0], pos[1] + ammoSize[i][1], pos[2]);
            glTexCoord2f(1.0, 0.0); glVertex3f(pos[0] - ammoSize[i][0], pos[1] - ammoSize[i][1], pos[2]);
            glTexCoord2f(0.0, 0.0); glVertex3f(pos[0] + ammoSize[i][0], pos[1] - ammoSize[i][1], pos[2]);
            glTexCoord2f(0.0, 1.0); glVertex3f(pos[0] + ammoSize[i][0], pos[1] + ammoSize[i][1], pos[2]);
          end;
      end;
      thisAmmo := thisAmmo.next; //Advance
    end;
    glEnd;
  end;
end;

function EnemyAmmo.getNewAmmo: ActiveAmmo;
var
  retAmmo: ActiveAmmo;
begin
  if Boolean(ammoPool.next) then
  begin
    retAmmo := ammoPool.next;
    ammoPool.next := retAmmo.next;
  end
  else
  begin
    retAmmo := ActiveAmmo.Create;
  end;

  Result := retAmmo;
end;

procedure EnemyAmmo.killAmmo(dead: ActiveAmmo);
begin
  dead.back := nil;
  dead.next := ammoPool.next;
  ammoPool.next := dead;
end;

procedure EnemyAmmo.loadTextures;
var
  filename: string;
  i: Integer;
begin
  for i := 0 to NUM_ENEMY_AMMO_TYPES - 1 do
  begin
    filename := 'png/enemyAmmo' + FormatCurr('0#', i) + '.png';
    LoadTexture(filename, ammoTex[i]);
  end;
end;

procedure EnemyAmmo.updateAmmo;
var
  _config: Config;
  i: Integer;
  thisAmmo: ActiveAmmo;
  backAmmo: ActiveAmmo;
  nextAmmo: ActiveAmmo;
begin
  _config := uniConfig.instance;

  for i := 0 to NUM_ENEMY_AMMO_TYPES - 1 do
  begin
    thisAmmo := ammoRoot[i].next;
    while Boolean(thisAmmo) do
    begin
      //-- clean up ammo
      if (thisAmmo.pos[0] > _config.screenBoundX) or
        (thisAmmo.pos[0] < -_config.screenBoundX) or
        (thisAmmo.pos[1] > _config.screenBoundY) or
        (thisAmmo.pos[1] < -_config.screenBoundY) then // remove ammo
      begin
        backAmmo := thisAmmo.back;
        nextAmmo := thisAmmo.next;
        backAmmo.next := nextAmmo;
        if Boolean(nextAmmo) then nextAmmo.back := backAmmo;
        killAmmo(thisAmmo);
        thisAmmo := nextAmmo; // Advance
      end
      else
      begin
        thisAmmo.updatePos;
        thisAmmo := thisAmmo.next; // Advance
      end;
    end;
  end;
end;

end.

